前一章已透過效能測試找出目標系統的容量、瓶頸與失敗方式。接下來要根據測試結果減少造成瓶頸的工作,再用相同條件確認調整是否有效。快取(Cache)只是其中一種方法,適合重複讀取或重複運算,不會自動改善所有效能問題。
導入快取後,同一項資料會同時存在原始來源與暫存副本。讀取速度可能提高,系統也要開始處理副本過期、更新失效、容量限制及快取故障。只有節省的處理成本大於新增的複雜度,快取才是合適的選擇。
壓力測試應該指出哪一段先超過門檻,以及等待時間、處理量與錯誤如何變化。只有看見整體回應變慢,還不足以決定加入快取。可以先按照瓶頸採取直接處理方式,再判斷重複工作是否值得保存結果。
| 測試觀察 | 要確認的原因 | 優先處理方式 | 快取可能提供的幫助 |
|---|---|---|---|
| 相同輸入反覆讀取同一內容 | 來源存取成本高,且短時間內結果相同 | 改善存取方式、縮小讀取範圍及合併重複讀取 | 重複使用先前結果,減少來源存取次數 |
| 相同輸入反覆執行昂貴運算 | 演算法、轉換或輸出產生耗時 | 改善演算法、移除重複步驟及預先計算 | 保存可重用的運算結果 |
| 相依項目回應緩慢或具有呼叫限制 | 每次操作都等待相同外部結果 | 合併呼叫、設定逾時及調整互動方式 | 在允許過期的期間內減少呼叫 |
| 寫入衝突或狀態更新排隊 | 多項工作競爭同一更新範圍 | 縮小一致性範圍、限制同時工作及調整流程 | 一般讀取快取通常無法消除寫入瓶頸 |
| 工作佇列、連線或執行單元已飽和 | 進入量超過可處理量,或重試放大負載 | 加入容量限制、反壓(Backpressure)、受控拒絕或調整部署容量 | 只有快取能實際減少該項工作時才有幫助 |
| 單次操作本身持續變慢 | 程式路徑、資料規模或相依狀態改變 | 先分析該次操作的各階段時間 | 缺少重複使用時,快取只會增加一次查找 |
如果問題來自缺少索引、無上限的資料讀取、未釋放的相依項目或持續重試,應該先修正直接原因。把昂貴且錯誤的工作結果放入快取,只會暫時降低它的發生次數,並且讓錯誤結果更難清除。
快取最適合「經常重複使用、產生成本高、變動頻率較低,而且允許短暫不是最新狀態」的內容。評估時應該以每一類資料或運算結果為單位,不能用「整個系統需要快取」取代個別判斷。
適合加入候選清單的內容通常符合下列條件:
下列情況通常不適合直接快取:
命中率不能在導入前直接假設。可以先從執行紀錄計算相同輸入的重複比例,再以代表性資料完成小型概念驗證(Proof of Concept, POC)。如果不同輸入幾乎各自只出現一次,就算單次來源讀取很慢,快取也未必能減輕壓力。
快取放得越接近工作入口,越可能省下完整處理路徑,但能安全共用的內容也越受限制。選擇位置前,要先確認系統是否具有相對應的互動方式與部署構成。
| 快取位置 | 適合情況 | 可以減少的工作 | 主要限制 |
|---|---|---|---|
| 用戶端私有快取 | 系統使用 HTTP,而且內容只供同一用戶端重用 | 傳輸及系統端的完整處理 | 無法由系統端集中控制所有副本,必須正確設定有效時間與重新驗證方式 |
| HTTP 共用快取或反向代理 | 多項 HTTP 要求可以安全共用相同回應 | 進入系統後的路由、讀取、運算及輸出產生 | 個人化或依請求條件變化的內容必須正確隔離,失效方式也受所選工具影響 |
| 處理程序內的本機快取 | 同一執行單位會重複使用內容,而且各副本短暫不同步仍可接受 | 跨程序查找及原始來源存取 | 多個執行單位會各自載入副本,容量、命中率與失效狀態也各自獨立 |
| 共用分散式快取 | 多個執行單位需要共用結果、集中設定到期或共同失效 | 原始來源存取與重複運算 | 每次查找仍有通訊及序列化成本,並新增需要部署、保護及監控的相依項目 |
| 多層快取 | 已證明少數熱門內容需要本機速度,同時又要共用第二層結果 | 本機層減少共用快取查找,共用層減少原始來源工作 | 兩層都要處理容量、到期與失效,整體一致性及故障情境更複雜 |
如果系統使用 HTTP,應該先確認協定本身的快取語意。MDN 的 HTTP 快取文件區分私有快取與共用快取,並說明 Cache-Control、Vary 及條件式要求的用途。Cache-Control: private 限制回應只能由私有快取保存,no-store 才是禁止保存。no-cache 代表重用前必須向來源重新驗證,不等於完全不保存。
同一網址如果會因語系、內容編碼或其他請求資訊產生不同回應,就要用 Vary 表達影響結果的標頭。變化維度過多會讓快取鍵幾乎無法重用。這時應該重新檢查快取位置與內容範圍,不能無限制地把所有請求資訊加入鍵值。
Redis 可以作為共用分散式快取,但「需要快取」不等於「需要 Redis」。如果目標系統只有一個執行單位、本機快取已能涵蓋重複工作,而且短暫遺失內容可以重新建立,先加入外部快取服務可能沒有足夠效益。
出現下列需求時,可以把 Redis 納入概念驗證:
概念驗證也要計入 Redis 無法使用、連線耗盡、項目遭淘汰、版本更新及監控維護的成本。Redis 在本章的責任是保存可重新建立的副本,原始來源仍保存正式狀態。Redis 的 Cache-Aside 文件也以來源更新後刪除快取鍵、讀取未命中時重新載入作為基本流程。
快取契約要說明甚麼內容可以被重用、何時失效、失敗時如何處理,以及如何證明結果仍然正確。這些規則如果只散落在程式中,後續很難判斷某個鍵能否安全刪除或延長有效時間。
快取鍵可以由功能名稱、資料版本、輸入條件及隔離範圍組成,例如:
report-summary:v3:locale=zh-TW:period=2026-08
v3 代表快取資料結構或計算規則版本。新版本無法讀取舊內容時,可以更換版本前綴,讓舊鍵自然到期。語系與期間會影響結果,因此必須納入鍵值。如果結果還會受到其他功能設定影響,也要納入相對應版本或條件。
鍵值不能包含密碼、權杖、私人金鑰或不必要的個人資料。記錄系統、錯誤訊息及監控工具經常會收集快取鍵,把敏感內容放入鍵值會擴大暴露範圍。如果內容會因身分或存取範圍而不同,應該使用無法直接還原敏感內容的穩定識別方式加以隔離,並且保留原有的存取檢查。
存活時間限制一個項目可以重用多久,應該根據可接受的過期時間、更新頻率及重新建立成本決定。所有項目使用相同的任意時間,可能讓重要內容過期太久,也可能讓穩定內容頻繁重新載入。
淘汰則是在容量達到限制時選擇先移除哪些項目。項目尚未到期,也可能因容量不足而遭淘汰。Redis 的鍵值淘汰文件提供 noeviction、allkeys-lru、allkeys-lfu 與只處理具有到期時間之鍵值的策略。選擇時要按照實際存取分布測試,不能把策略名稱當成適用結論。
容量規劃至少要記錄項目數量、平均與較大項目的序列化大小、預期熱門資料範圍及容量上限。到期時間與容量淘汰都只能移除副本,不能用來刪除唯一資料。
保存複合內容時,要定義序列化格式、資料結構版本與必要欄位。讀取到未知版本、解析失敗或缺少欄位時,應該將它視為未命中並移除損壞項目,再從原始來源重建。不能把解析錯誤直接當成正常空值,否則功能結果會在快取啟用後改變。
旁路快取(Cache-Aside)讓系統程式自行管理快取與原始來源,是常見且容易逐項導入的方式:
到期時間是避免副本無限期過期的最後限制,不能取代更新時的主動失效。如果需求只允許一分鐘的過期狀態,設定一分鐘存活時間仍不保證更新後立刻一致。需要立即反映更新時,應該在更新流程中刪除或更新快取,並驗證失效失敗時的處理方式。
一項更新可能影響多個彙整結果。此時要記錄來源內容與快取鍵或標籤之間的關係,不能只刪除最明顯的一個鍵。受影響範圍難以正確列出時,可以使用較粗的版本前綴讓整組內容失效,代價是下一次讀取要重新建立更多項目。
更新與未命中載入同時發生時,也可能出現競爭:讀取流程先取得舊內容,更新流程完成並刪除快取,先前的讀取流程卻在刪除後把舊內容寫回。風險超過容許範圍時,應該使用來源版本進行條件式寫入、讓鍵值包含資料修訂版本,或協調同一鍵的載入與失效順序,並以測試重現這項競爭。
快取正常時減少的工作,可能在內容到期或快取故障時一次回到原始來源。設計時要用冷快取、同時到期與故障情境確認來源仍受保護。
| 情況 | 造成的壓力 | 處理方式 |
|---|---|---|
| 大量要求查找不存在的內容 | 每次都未命中並重複讀取原始來源 | 先驗證輸入。允許短暫過期時,可以暫存「不存在」的結果,並使用較短存活時間 |
| 熱門鍵到期後同時被讀取 | 多項工作同時重建相同內容,形成大量同時未命中(Cache Stampede) | 合併同一鍵的載入工作,只允許一項工作重建,其餘等待同一結果。等待及鎖定都要有逾時 |
| 大量鍵在相同時間到期 | 原始來源在短時間接收所有重新載入工作 | 按照允許範圍加入到期時間差異,分批預先載入,並限制重新建立的同時工作量 |
| 少數鍵承受大部分讀取 | 單一快取分割或連線路徑仍可能成為瓶頸 | 個別觀察熱門鍵,評估本機第一層、結果拆分或受控複本是否能改善,而且不破壞失效規則 |
| 快取整體無法使用 | 所有讀取直接回到原始來源 | 設定快速失敗、限制降級時的同時讀取,並在來源容量不足時受控拒絕或提供已核准的降級結果 |
等待舊值可以作為某些讀取的降級方式,但前提是需求明確允許。對必須即時一致或影響重要狀態變更的內容,不能為了維持回應速度就回傳已過期結果。
快取是效能元件,不應在沒有明確決策的情況下成為功能成立的必要條件。每一類快取內容都要定義故障時採取哪一種結果:
如果快取失效就會使原始來源超過容量,單純「發生錯誤時繞過快取」並不安全。降級路徑要與容量限制、反壓及受控拒絕一起測試。恢復時也要限制重新載入速度,避免所有項目同時暖機形成第二次尖峰。
快取內容仍要遵守原始資料的保護要求。除非已有明確需求與保護措施,不應保存密碼、權杖、私人金鑰或不必要的個人資料。如果結果包含已確認的存取限制,快取命中不能略過原本的檢查,清除與保存期限也要符合原始內容的處理規則。
導入前要先寫下改善假設,例如「將重複彙整結果保存五分鐘,可讓來源讀取次數降低 70%,同時維持既有功能結果」。完成後使用與前一章相同的環境、資料、操作比例、負載階段及門檻,比較未啟用與啟用快取的結果。
至少要觀察下列指標:
功能測試要涵蓋命中、未命中、到期、來源更新、同時未命中、容量淘汰、快取故障與資料版本變更。效能測試則要分開保存冷快取與暖快取結果。只測量全部預先載入後的最佳情況,無法證明部署、故障恢復或內容更新期間仍符合門檻。
高命中率不等於改善成功。如果命中內容本來就很便宜,或序列化與通訊成本過高,來源壓力與整體回應可能沒有明顯改善。相反地,少量但成本極高的工作即使命中比例不高,也可能值得快取。最終決定要同時看功能正確性、節省的來源工作、整體容量與新增維護成本。